{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Food Manufacture II\n",
    "\n",
    "## Objective and Prerequisites\n",
    "\n",
    "In this example, you’ll have to tackle the same problem that you did in “Food Manufacturing I,” but with additional constraints that change the problem type from a linear program (LP) problem to a mixed-integer program (MIP) problem, making it harder to solve.\n",
    "\n",
    "More information on this type of model can be found in example #2 of the fifth edition of Modeling Building in Mathematical Programming by H. P. Williams on pages 255 and 299 – 300.\n",
    "\n",
    "This modeling example is at the intermediate level, where we assume that you know Python and are familiar with the Gurobi Python API. In addition, you should have some knowledge about building mathematical optimization models.\n",
    "\n",
    "**Download the Repository** <br /> \n",
    "You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip). \n",
    "\n",
    "---\n",
    "## Problem Description\n",
    "\n",
    "A manufacturer needs to refine several raw oils and blend them together to produce a given food product that can be sold. The raw oils needed can be divided into two categories:\n",
    "\n",
    "\n",
    "| Category        | Oil         |\n",
    "| ------------- |-------------| \n",
    "| Vegetable oils:|  VEG 1<br>VEG 2 | \n",
    "| Non-vegetable oils:     | OIL 1<br>OIL 2<br>OIL 3      |\n",
    "\n",
    "\n",
    "The manufacturer can choose to buy raw oils for the current month and/or buy them on the futures market for delivery in a subsequent month. Prices for immediate delivery and in the futures market are given below in USD/ton:\n",
    "\n",
    "| Month |\tVEG 1 |\tVEG 2 |\tOIL 1 |\tOIL 2 |\tOIL 3|\n",
    "| ------------- |-------------| -------------| -------------| -------------| -------------| \n",
    "| January| 110 |\t120 |\t130 |\t110 |\t115|\n",
    "| February |130 |\t130 |\t110 |\t90|\t115|\n",
    "| March |110 |\t140 |\t130 |\t100 |\t95|\n",
    "| April |120 |\t110 |\t120 |\t120 |\t125|\n",
    "| May | 100 |\t120 |\t150 |\t110 |\t105|\n",
    "| June |  \t90 |\t100 |\t140 |\t80| \t135 |\n",
    "\n",
    "There are a number of additional factors that must be taken into account. These include:\n",
    "\n",
    "1. The final food product sells for $\\$150$ per ton.\n",
    "2. Each category of oil (vegetable and non-vegetable) needs to be refined on a different production line.\n",
    "3. There is limited refinement capacity such that in any given month a maximum of 200 tons of vegetable oils and 250 tons of non-vegetable oils can be refined.\n",
    "4. Also, there is no waste in the refinement process, so the sum of the raw oils refined will equal the amount of refined oils available.\n",
    "5. The cost of refining the oils may be ignored.\n",
    "\n",
    "In addition to the refining limits above, there are limits to the amount of raw oils that can be stored for future use, and there is a cost associated with each ton of oil stored. The limit is 1,000 tons of each raw oil and the storage cost is $\\$5$ per ton per month. The manufacturer cannot store the produced food product or the refined oils.\n",
    "\n",
    "The final food product must have a hardness between three and six on a given hardness scale. For the purposes of the model, hardness blends linearly and the hardness of each raw oil is:\n",
    "\n",
    "|Oils |\tHardness|\n",
    "| ------------- |-------------| \n",
    "|VEG 1 |\t8.8|\n",
    "|VEG 2 |\t6.1|\n",
    "|OIL 1 |\t2.0|\n",
    "|OIL2 |\t4.2|\n",
    "|OIL 3| \t5.0|\n",
    "\n",
    "At the start of January, there are 500 tons of each type of raw oil in storage. For the purpose of the model, this should also be the level of raw oils in storage at the end of June.\n",
    "\n",
    "This version of the Food Manufacture problem adds the following additional constraints to the first version:\n",
    "\n",
    "- Condition 1: If an oil is used during a month, the minimum quantity used must be 20 tons.\n",
    "- Condition 2: The maximum number of oils used in a month is three.\n",
    "- Condition 3: The use of VEG1 or VEG2 in a given month requires the use of OIL3 in that same month.\n",
    "\n",
    "\n",
    "Given the above information, what monthly buying and manufacturing decisions should be made in order to maximize profit?\n",
    "\n",
    "---\n",
    "## Model Formulation\n",
    "\n",
    "### Sets and Indices\n",
    "\n",
    "$t \\in \\text{Months}=\\{\\text{Jan},\\text{Feb},\\text{Mar},\\text{Apr},\\text{May},\\text{Jun}\\}$: Set of months.\n",
    "\n",
    "$V=\\{\\text{VEG1},\\text{VEG2}\\}$: Set of vegetable oils.\n",
    "\n",
    "$N=\\{\\text{OIL1},\\text{OIL2},\\text{OIL3}\\}$: Set of non-vegetable oils.\n",
    "\n",
    "$o \\in \\text{Oils} = V \\cup N$: Set of oils.\n",
    "\n",
    "### Parameters\n",
    "\n",
    "$\\text{price} \\in \\mathbb{R}^+$: Sale price of the final product.\n",
    "\n",
    "$\\text{init_store} \\in \\mathbb{R}^+$: Initial storage amount in tons.\n",
    "\n",
    "$\\text{target_store} \\in \\mathbb{R}^+$: Target storage amount in tons.\n",
    "\n",
    "$\\text{holding_cost} \\in \\mathbb{R}^+$: Monthly cost (in USD/ton/month) of keeping in inventory a ton of oil.\n",
    "\n",
    "$\\text{min_consume} \\in \\mathbb{R}^+$: Minimum number of tons to consume of a given oil in a month.\n",
    "\n",
    "$\\text{veg_cap} \\in \\mathbb{R}^+$: Installed capacity (in tons) to refine vegetable oils.\n",
    "\n",
    "$\\text{oil_cap} \\in \\mathbb{R}^+$: Installed capacity (in tons) to refine non-vegetable oils.\n",
    "\n",
    "$\\text{min_hardness} \\in \\mathbb{R}^+$: lowest hardness allowed for the final product.\n",
    "\n",
    "$\\text{max_hardness} \\in \\mathbb{R}^+$: highest hardness allowed for the final product.\n",
    "\n",
    "$\\text{hardness}_o \\in \\mathbb{R}^+$: Hardness of oil $o$.\n",
    "\n",
    "$\\text{max_ingredients} \\in \\mathbb{N}$: Maximum number of oil types to consume in a given month.\n",
    "\n",
    "$\\text{cost}_{t,o} \\in \\mathbb{R}^+$: Estimated purchase price for oil $o$ at month $t$.\n",
    "\n",
    "\n",
    "### Decision Variables\n",
    "\n",
    "$\\text{produce}_t \\in \\mathbb{R}^+$: Tons of food to produce at month $t$.\n",
    "\n",
    "$\\text{buy}_{t,o} \\in \\mathbb{R}^+$: Tons of oil $o$ to buy at month $t$.\n",
    "\n",
    "$\\text{consume}_{t,o} \\in \\mathbb{R}^+$: Tons of oil $o$ to use at month $t$.\n",
    "\n",
    "$\\text{store}_{t,o} \\in \\mathbb{R}^+$: Tons of oil $o$ to store at month $t$.\n",
    "\n",
    "$\\text{use}_{t,o} \\in \\{0,1\\}$: 1 if oil $o$ is used on month $t$, 0 otherwise. \n",
    "\n",
    "\n",
    "### Objective Function\n",
    "\n",
    "- **Profit**: Maximize the total profit (in USD) of the planning horizon.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{Maximize} \\quad Z = \\sum_{t \\in \\text{Months}}\\text{price}*\\text{produce}_t - \\sum_{t \\in \\text{Months}}\\sum_{o \\in \\text{Oils}}(\\text{cost}_{t,o}*\\text{consume}_{t,o} + \\text{holding_cost}*\\text{store}_{t,o})\n",
    "\\tag{0}\n",
    "\\end{equation}\n",
    "\n",
    "### Constraints\n",
    "\n",
    "- **Initial Balance:** The Tons of oil $o$ purchased in January and the ones previously stored should be equal to the Tons of said oil consumed and stored in that month.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{init store} + \\text{buy}_{Jan,o} = \\text{consume}_{Jan,o} + \\text{store}_{Jan,o} \\quad \\forall o \\in \\text{Oils}\n",
    "\\tag{1}\n",
    "\\end{equation}\n",
    "\n",
    "- **Balance:** The Tons of oil $o$ purchased in month $t$ and the ones previously stored should be equal to the Tons of said oil consumed and stored in that month.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{store}_{t-1,o} + \\text{buy}_{t,o} = \\text{consume}_{t,o} + \\text{store}_{t,o} \\quad \\forall (t,o) \\in \\text{Months} \\setminus \\{\\text{Jan}\\} \\times \\text{Oils}\n",
    "\\tag{2}\n",
    "\\end{equation}\n",
    "\n",
    "- **Inventory Target**: The Tons of oil $o$ kept in inventory at the end of the planning horizon should hit the target.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{store}_{Jun,o} = \\text{target_store} \\quad \\forall o \\in \\text{Oils}\n",
    "\\tag{3}\n",
    "\\end{equation}\n",
    "\n",
    "- **Refinement Capacity**: Total Tons of oil $o$ consumed in month $t$ cannot exceed the refinement capacity.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{o \\in V}\\text{consume}_{t,o} \\leq \\text{veg_cap} \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{4.1}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{o \\in N}\\text{consume}_{t,o} \\leq \\text{oil_cap} \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{4.2}\n",
    "\\end{equation}\n",
    "\n",
    "- **Hardness**: The hardness value of the food produced in month $t$ should be within tolerances.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{min_hardness}*\\text{produce}_t \\leq \\sum_{o \\in \\text{Oils}} \\text{hardness}_o*\\text{consume}_{t,o} \\leq \\text{max_hardness}*\\text{produce}_t \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{5}\n",
    "\\end{equation}\n",
    "\n",
    "- **Mass Conservation**: Total Tons of oil consumed in month $t$ should be equal to the Tons of the food produced in that month.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{o \\in \\text{Oils}}\\text{consume}_{t,o} = \\text{produce}_t \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{6}\n",
    "\\end{equation}\n",
    "\n",
    "- **Consumption Range**: Oil $o$ can be consumed in month $t$ if we decide to use it in that month, and the Tons consumed should be between 20 and the refinement capacity for its type. \n",
    "\n",
    "\\begin{equation}\n",
    "\\text{min_consume}*\\text{use}_{t,o} \\leq \\text{consume}_{t,o} \\leq \\text{veg_cap}*\\text{use}_{t,o} \\quad \\forall (t,o) \\in V \\times \\text{Months}\n",
    "\\tag{7.1}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{min_consume}*\\text{use}_{t,o} \\leq \\text{consume}_{t,o} \\leq \\text{oil_cap}*\\text{use}_{t,o} \\quad \\forall (t,o) \\in N \\times \\text{Months}\n",
    "\\tag{7.2}\n",
    "\\end{equation}\n",
    "\n",
    "- **Recipe**: The maximum number of oils used in month $t$ must be three.\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_{o \\in \\text{Oils}}\\text{use}_{t,o} \\leq \\text{max_ingredients} \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{8}\n",
    "\\end{equation}\n",
    "\n",
    "- **If-then Constraint**: If oils VEG1 or VEG2 are used in month $t$, then OIL3 must be used in that month.\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{use}_{t,\\text{VEG1}} \\leq \\text{use}_{t,\\text{OIL3}} \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{9.1}\n",
    "\\end{equation}\n",
    "\n",
    "\\begin{equation}\n",
    "\\text{use}_{t,\\text{VEG2}} \\leq \\text{use}_{t,\\text{OIL3}} \\quad \\forall t \\in \\text{Months}\n",
    "\\tag{9.2}\n",
    "\\end{equation}\n",
    "\n",
    "---\n",
    "## Python Implementation\n",
    "We import the Gurobi Python Module and other Python libraries.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install gurobipy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import gurobipy as gp\n",
    "from gurobipy import GRB\n",
    "\n",
    "# tested with Python 3.7 & Gurobi 9"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Input Data\n",
    "We define all the input data of the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Parameters\n",
    "\n",
    "months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"]\n",
    "\n",
    "oils = [\"VEG1\", \"VEG2\", \"OIL1\", \"OIL2\", \"OIL3\"]\n",
    "\n",
    "cost = {\n",
    "    ('Jan', 'VEG1'): 110,\n",
    "    ('Jan', 'VEG2'): 120,\n",
    "    ('Jan', 'OIL1'): 130,\n",
    "    ('Jan', 'OIL2'): 110,\n",
    "    ('Jan', 'OIL3'): 115,\n",
    "    ('Feb', 'VEG1'): 130,\n",
    "    ('Feb', 'VEG2'): 130,\n",
    "    ('Feb', 'OIL1'): 110,\n",
    "    ('Feb', 'OIL2'): 90,\n",
    "    ('Feb', 'OIL3'): 115,\n",
    "    ('Mar', 'VEG1'): 110,\n",
    "    ('Mar', 'VEG2'): 140,\n",
    "    ('Mar', 'OIL1'): 130,\n",
    "    ('Mar', 'OIL2'): 100,\n",
    "    ('Mar', 'OIL3'): 95,\n",
    "    ('Apr', 'VEG1'): 120,\n",
    "    ('Apr', 'VEG2'): 110,\n",
    "    ('Apr', 'OIL1'): 120,\n",
    "    ('Apr', 'OIL2'): 120,\n",
    "    ('Apr', 'OIL3'): 125,\n",
    "    ('May', 'VEG1'): 100,\n",
    "    ('May', 'VEG2'): 120,\n",
    "    ('May', 'OIL1'): 150,\n",
    "    ('May', 'OIL2'): 110,\n",
    "    ('May', 'OIL3'): 105,\n",
    "    ('Jun', 'VEG1'): 90,\n",
    "    ('Jun', 'VEG2'): 100,\n",
    "    ('Jun', 'OIL1'): 140,\n",
    "    ('Jun', 'OIL2'): 80,\n",
    "    ('Jun', 'OIL3'): 135\n",
    "}\n",
    "\n",
    "\n",
    "hardness = {\"VEG1\": 8.8, \"VEG2\": 6.1, \"OIL1\": 2.0, \"OIL2\": 4.2, \"OIL3\": 5.0}\n",
    "\n",
    "price = 150\n",
    "init_store = 500\n",
    "veg_cap = 200\n",
    "oil_cap = 250\n",
    "\n",
    "min_hardness = 3\n",
    "max_hardness = 6\n",
    "max_ingredients = 3\n",
    "holding_cost = 5\n",
    "min_consume = 20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Deployment\n",
    "\n",
    "For each period, we create a variable which will take into account the value of the food produced. For each product (five kinds of oils) and each period we will create variables for the amount that gets purchased, used, and stored.\n",
    "\n",
    "For each period and each product, we need a binary variable, which indicates if this product is used in the current period."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using license file c:\\gurobi\\gurobi.lic\n"
     ]
    }
   ],
   "source": [
    "food = gp.Model('Food Manufacture II')\n",
    "# Quantity of food produced in each period\n",
    "produce = food.addVars(months, name=\"Food\")\n",
    "# Quantity bought of each product in each period\n",
    "buy = food.addVars(months, oils, name = \"Buy\")\n",
    "# Quantity used of each product  in each period\n",
    "consume = food.addVars(months, oils, name = \"Consume\")\n",
    "# Quantity stored of each product  in each period\n",
    "store = food.addVars(months, oils, name = \"Store\")\n",
    "# binary variables =1, if consume > 0\n",
    "use = food.addVars(months, oils, vtype=GRB.BINARY, name = \"Use\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we insert the constraints. The balance constraints ensure that the amount of oil that is in the storage in the previous period plus the amount that gets purchased equals the amount that is used plus the amount that is stored in the current period (for each oil). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#1. Initial Balance\n",
    "Balance0 = food.addConstrs((init_store + buy[months[0], oil]\n",
    "                 == consume[months[0], oil] + store[months[0], oil]\n",
    "                 for oil in oils), \"Initial_Balance\")\n",
    "\n",
    "#2.  Balance\n",
    "Balance = food.addConstrs((store[months[months.index(month)-1], oil] + buy[month, oil]\n",
    "                 == consume[month, oil] + store[month, oil]\n",
    "                 for oil in oils for month in months if month != months[0]), \"Balance\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Inventory Target constraints force that at the end of the last period the storage contains the initial amount of each oil. The problem description demands that the storage is as full as in the beginning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#3. Inventory Target\n",
    "TargetInv = food.addConstrs((store[months[-1], oil] == init_store for oil in oils), \"End_Balance\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The capacity constraints restrict the amount of veg and non-veg oils which can be processed per period. Per month only 200 tons of vegetable oil and 250 tons of non-vegetable oil can be processed to the final product."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#4.1 Vegetable Oil Capacity\n",
    "VegCapacity = food.addConstrs((gp.quicksum(consume[month, oil] for oil in oils if \"VEG\" in oil)\n",
    "                 <= veg_cap for month in months), \"Capacity_Veg\")\n",
    "\n",
    "#4.2 Non-vegetable Oil Capacity\n",
    "NonVegCapacity = food.addConstrs((gp.quicksum(consume[month, oil] for oil in oils if \"OIL\" in oil)\n",
    "                 <= oil_cap for month in months), \"Capacity_Oil\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The hardness constraints limit the hardness of the final product, which needs to remain between 3 and 6. Each oil has a certain hardness. The final product may be made up of different oils. The hardness of the final product is measured by the hardness of each ingredient multiplied by its share of the final product. It is assumed that the hardness blends linearly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#5. Hardness\n",
    "HardnessMin = food.addConstrs((gp.quicksum(hardness[oil]*consume[month, oil] for oil in oils)\n",
    "                 >= min_hardness*produce[month] for month in months), \"Hardness_lower\")\n",
    "HardnessMax = food.addConstrs((gp.quicksum(hardness[oil]*consume[month, oil] for oil in oils)\n",
    "                 <= max_hardness*produce[month] for month in months), \"Hardness_upper\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Mass Conservation constraints ensure that the amount of products used in each period equals the amount of food produced in that period. This ensures that all oil that is used is also processed into the final product (food)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "#6. Mass Conservation\n",
    "MassConservation = food.addConstrs((consume.sum(month) == produce[month] for month in months), \"Mass_conservation\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Condition 1 constraints force that if any product is used in any period then at least 20 tons is used. They also force that the binary variable for each product and each month is set to one if and only if the continuous variable used for the same product and the same month is non-zero. The binary variable is called an indicator variable since it is linked to a continuous variable and indicates if it is non-zero.\n",
    "\n",
    "It's relatively straightforward to express Condition 1 as a pure MIP constraint set. Let's see how to model this set using Gurobi’s general constraints (from version 7.0 onwards):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#7.1 & 7.2 Consumption Range - Using Gurobi's General Constraints\n",
    "for month in months:\n",
    "    for oil in oils:\n",
    "        food.addGenConstrIndicator(use[month, oil], 0,\n",
    "                                   consume[month, oil] == 0,\n",
    "                                   name=\"Lower_bound_{}_{}\".format(month, oil))\n",
    "        food.addGenConstrIndicator(use[month, oil], 1,\n",
    "                                   consume[month, oil] >=  min_consume,\n",
    "                                   name=\"Upper_bound_{}_{}\".format(month, oil))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Condition 2 constraints ensure that each final product is only made up of at most three ingredients."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "#8. Recipe\n",
    "condition2 = food.addConstrs((use.sum(month) <= max_ingredients for month in months),\"Recipe\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Condition 3 constraints ensure that if vegetable one or vegetable two are used, then oil three must also be used. We will use again Gurobi's general constraints:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "#9.1 & 9.2 If-then Constraint\n",
    "for month in months:\n",
    "    food.addGenConstrIndicator(use[month, \"VEG1\"], 1,\n",
    "                               use[month, \"OIL3\"] == 1,\n",
    "                               name = \"If_then_a_{}\".format(month))\n",
    "    food.addGenConstrIndicator(use[month, \"VEG2\"], 1,\n",
    "                               use[month, \"OIL3\"] == 1,\n",
    "                               name = \"If_then_b_{}\".format(month))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The objective is to maximize the profit of the company. This is calculated as revenue minus costs for buying and storing of the purchased products (ingredients)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "#0. Objective Function\n",
    "obj = price*produce.sum() - buy.prod(cost) - holding_cost*store.sum()\n",
    "food.setObjective(obj, GRB.MAXIMIZE) # maximize profit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we start the optimization and Gurobi finds the optimal solution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)\n",
      "Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n",
      "Optimize a model with 71 rows, 126 columns and 288 nonzeros\n",
      "Model fingerprint: 0x9d193fb2\n",
      "Model has 72 general constraints\n",
      "Variable types: 96 continuous, 30 integer (30 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 9e+00]\n",
      "  Objective range  [5e+00, 2e+02]\n",
      "  Bounds range     [1e+00, 1e+00]\n",
      "  RHS range        [3e+00, 5e+02]\n",
      "Presolve added 24 rows and 0 columns\n",
      "Presolve removed 0 rows and 66 columns\n",
      "Presolve time: 0.01s\n",
      "Presolved: 95 rows, 60 columns, 272 nonzeros\n",
      "Variable types: 35 continuous, 25 integer (25 binary)\n",
      "Found heuristic solution: objective 34650.000000\n",
      "\n",
      "Root relaxation: objective 1.056500e+05, 57 iterations, 0.00 seconds\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "     0     0 105650.000    0    6 34650.0000 105650.000   205%     -    0s\n",
      "H    0     0                    98928.703704 105650.000  6.79%     -    0s\n",
      "     0     0 105650.000    0    8 98928.7037 105650.000  6.79%     -    0s\n",
      "H    0     0                    99516.666667 105650.000  6.16%     -    0s\n",
      "H    0     0                    99966.666667 105650.000  5.69%     -    0s\n",
      "     0     0 105650.000    0    9 99966.6667 105650.000  5.69%     -    0s\n",
      "H    0     0                    100053.70370 105650.000  5.59%     -    0s\n",
      "H    0     0                    100191.66667 105650.000  5.45%     -    0s\n",
      "     0     0 105650.000    0    6 100191.667 105650.000  5.45%     -    0s\n",
      "H    0     0                    100278.70370 105650.000  5.36%     -    0s\n",
      "     0     0 105650.000    0    6 100278.704 105650.000  5.36%     -    0s\n",
      "     0     2 105650.000    0    6 100278.704 105650.000  5.36%     -    0s\n",
      "\n",
      "Cutting planes:\n",
      "  Gomory: 1\n",
      "  Implied bound: 5\n",
      "  MIR: 8\n",
      "  Flow cover: 7\n",
      "  Inf proof: 2\n",
      "\n",
      "Explored 442 nodes (2628 simplex iterations) in 0.13 seconds\n",
      "Thread count was 8 (of 8 available processors)\n",
      "\n",
      "Solution count 7: 100279 100192 100054 ... 34650\n",
      "\n",
      "Optimal solution found (tolerance 1.00e-04)\n",
      "Best objective 1.002787037037e+05, best bound 1.002787037037e+05, gap 0.0000%\n"
     ]
    }
   ],
   "source": [
    "food.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Analysis\n",
    "\n",
    "When originally designed, this model proved comparatively hard to solve (see Food Manufacture I). The profit (revenue from sales minus cost of raw oils) resulting from this plan is $\\$100,278.7$. There are alternative — and equally good — solutions.\n",
    "\n",
    "### Purchase Plan\n",
    "\n",
    "This plan defines the amount of vegetable oil (VEG) and non-vegetable oil (OIL) that we need to purchase during the planning horizon. For example, 480.4 tons of vegetable oil of type VEG1 needs to be bought in June."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>VEG1</th>\n",
       "      <th>VEG2</th>\n",
       "      <th>OIL1</th>\n",
       "      <th>OIL2</th>\n",
       "      <th>OIL3</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>190.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>540.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>480.4</td>\n",
       "      <td>629.6</td>\n",
       "      <td>0.0</td>\n",
       "      <td>730.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      VEG1   VEG2  OIL1   OIL2   OIL3\n",
       "Jan    0.0    0.0   0.0    0.0    0.0\n",
       "Feb    0.0    0.0   0.0  190.0    0.0\n",
       "Mar    0.0    0.0   0.0    0.0  540.0\n",
       "Apr    0.0    0.0   0.0    0.0    0.0\n",
       "May    0.0    0.0   0.0    0.0   40.0\n",
       "Jun  480.4  629.6   0.0  730.0    0.0"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = oils.copy()\n",
    "purchase_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, oil in buy.keys():\n",
    "    if (abs(buy[month, oil].x) > 1e-6):\n",
    "        purchase_plan.loc[month, oil] = np.round(buy[month, oil].x, 1)\n",
    "purchase_plan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Monthly Consumption\n",
    "\n",
    "This plan determines the amount of vegetable oil (VEG) and non-vegetable oil (OIL) consumed during the planning horizon. For example, 114.8 tons of vegetable oil of type VEG2 is consumed in January."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>VEG1</th>\n",
       "      <th>VEG2</th>\n",
       "      <th>OIL1</th>\n",
       "      <th>OIL2</th>\n",
       "      <th>OIL3</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>0.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>230.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>85.2</td>\n",
       "      <td>114.8</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>250.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>85.2</td>\n",
       "      <td>114.8</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>250.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>155.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>230.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>155.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>230.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>0.0</td>\n",
       "      <td>200.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>230.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      VEG1   VEG2  OIL1   OIL2   OIL3\n",
       "Jan    0.0  200.0   0.0  230.0   20.0\n",
       "Feb   85.2  114.8   0.0    0.0  250.0\n",
       "Mar   85.2  114.8   0.0    0.0  250.0\n",
       "Apr  155.0    0.0   0.0  230.0   20.0\n",
       "May  155.0    0.0   0.0  230.0   20.0\n",
       "Jun    0.0  200.0   0.0  230.0   20.0"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = oils.copy()\n",
    "reqs = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, oil in consume.keys():\n",
    "    if (abs(consume[month, oil].x) > 1e-6):\n",
    "        reqs.loc[month, oil] = np.round(consume[month, oil].x, 1)\n",
    "reqs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inventory Plan\n",
    "\n",
    "This plan reflects the amount of vegetable oil (VEG) and non-vegetable oil (OIL) in inventory at the end of each period of  the planning horizon. For example, at the end of February we have 500 tons of Non-vegetable oil of type OIL1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>VEG1</th>\n",
       "      <th>VEG2</th>\n",
       "      <th>OIL1</th>\n",
       "      <th>OIL2</th>\n",
       "      <th>OIL3</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Jan</th>\n",
       "      <td>500.0</td>\n",
       "      <td>300.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>270.0</td>\n",
       "      <td>480.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feb</th>\n",
       "      <td>414.8</td>\n",
       "      <td>185.2</td>\n",
       "      <td>500.0</td>\n",
       "      <td>460.0</td>\n",
       "      <td>230.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Mar</th>\n",
       "      <td>329.6</td>\n",
       "      <td>70.4</td>\n",
       "      <td>500.0</td>\n",
       "      <td>460.0</td>\n",
       "      <td>520.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Apr</th>\n",
       "      <td>174.6</td>\n",
       "      <td>70.4</td>\n",
       "      <td>500.0</td>\n",
       "      <td>230.0</td>\n",
       "      <td>500.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>May</th>\n",
       "      <td>19.6</td>\n",
       "      <td>70.4</td>\n",
       "      <td>500.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>520.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Jun</th>\n",
       "      <td>500.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>500.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      VEG1   VEG2   OIL1   OIL2   OIL3\n",
       "Jan  500.0  300.0  500.0  270.0  480.0\n",
       "Feb  414.8  185.2  500.0  460.0  230.0\n",
       "Mar  329.6   70.4  500.0  460.0  520.0\n",
       "Apr  174.6   70.4  500.0  230.0  500.0\n",
       "May   19.6   70.4  500.0    0.0  520.0\n",
       "Jun  500.0  500.0  500.0  500.0  500.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = months.copy()\n",
    "columns = oils.copy()\n",
    "store_plan = pd.DataFrame(columns=columns, index=rows, data=0.0)\n",
    "\n",
    "for month, oil in store.keys():\n",
    "    if (abs(store[month, oil].x) > 1e-6):\n",
    "        store_plan.loc[month, oil] = np.round(store[month, oil].x, 1)\n",
    "store_plan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: If you want to write your solution to a file, rather than print it to the terminal, you can use the model.write() command. An example implementation is:\n",
    "\n",
    "`food.write(\"food-manufacture-2-output.sol\")`\n",
    "\n",
    "---\n",
    "## References\n",
    "\n",
    "H. Paul Williams, Model Building in Mathematical Programming, fifth edition.\n",
    "\n",
    "Copyright © 2020 Gurobi Optimization, LLC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
